package models

import (
	"fmt"
	"math/rand"
	"net"
	"time"
)

type E地图内容类别 uint8

const (
	E类别_怪物  = 0x01
	E类别_NPC = 0x02
	E类别_物品  = 0x04
	E类别_人物  = 0x08
	E类别_技能  = 0x10
	E类别_宠物  = 0x20
)

var (
	E基础属性_当前血量 = uint16(0)
	E基础属性_当前蓝量 = uint16(0)
	E基础属性_总血量  = uint16(0)
	E基础属性_总蓝量  = uint16(0)

	E基础属性_最小物理攻击力   = uint16(0)
	E基础属性_最大物理攻击力   = uint16(0)
	E基础属性_最小魔法攻击力   = uint16(0)
	E基础属性_最大魔法攻击力   = uint16(0)
	E基础属性_物理防御力     = uint16(0)
	E基础属性_物理防御千分比   = uint16(0)
	E基础属性_魔法防御力     = uint16(0)
	E基础属性_魔法防御千分比   = uint16(0)
	E基础属性_必杀成功率     = uint16(0)
	E基础属性_魔法必杀成功率   = uint16(0)
	E基础属性_眩晕        = uint16(0)
	E基础属性_格挡成功率     = uint16(0)
	E基础属性_降低对方格挡成功率 = uint16(0)
	E基础属性_攻击成功率     = uint16(0)
	E基础属性_生命恢复力     = uint16(0)
	E基础属性_魔法恢复力     = uint16(0)
	E基础属性_全体物品获得率   = uint16(0)
	E基础属性_魔法物品获得率   = uint16(0)
	E基础属性_移动速度      = uint16(0)
	E基础属性_力量        = uint16(0)
	E基础属性_敏捷        = uint16(0)
	E基础属性_精神        = uint16(0)
	E基础属性_白魔法       = uint16(0)
	E基础属性_红魔法       = uint16(0)
	E基础属性_蓝魔法       = uint16(0)
	E基础属性_黄魔法       = uint16(0)
	E基础属性_黑魔法       = uint16(0)

	E基础属性_魔法抵抗力  = uint16(0)
	E基础属性_红魔法抵抗力 = uint16(0)
	E基础属性_蓝魔法抵抗力 = uint16(0)
	E基础属性_黄魔法抵抗力 = uint16(0)
	E基础属性_物理反弹   = uint16(0)
	E基础属性_魔法反弹   = uint16(0)
	E基础属性_集中力    = uint16(0)
	E基础属性_分散力    = uint16(0)
	E基础属性_攻击速度   = uint16(0)

	E基础属性_亡灵系最小攻击力 = uint16(0)
	E基础属性_亡灵系最大攻击力 = uint16(0)
	E基础属性_亡灵系防御率   = uint16(0)
	E基础属性_亡灵系防御力   = uint16(0)

	E基础属性_生物系最小攻击力 = uint16(0)
	E基础属性_生物系最大攻击力 = uint16(0)
	E基础属性_生物系防御率   = uint16(0)
	E基础属性_生物系防御力   = uint16(0)

	E基础属性_召唤系最小攻击力 = uint16(0)
	E基础属性_召唤系最大攻击力 = uint16(0)
	E基础属性_召唤系防御率   = uint16(0)
	E基础属性_召唤系防御力   = uint16(0)

	E基础属性_突变系最小攻击力 = uint16(0)
	E基础属性_突变系最大攻击力 = uint16(0)
	E基础属性_突变系防御率   = uint16(0)
	E基础属性_突变系防御力   = uint16(0)

	E基础属性_人类系最小攻击力 = uint16(0)
	E基础属性_人类系最大攻击力 = uint16(0)
	E基础属性_人类系防御率   = uint16(0)
	E基础属性_人类系防御力   = uint16(0)

	E基础属性_精灵系最小攻击力 = uint16(0)
	E基础属性_精灵系最大攻击力 = uint16(0)
	E基础属性_精灵系防御率   = uint16(0)
	E基础属性_精灵系防御力   = uint16(0)

	E基础属性_等级           = uint16(0)
	E基础属性_受损的生命转为法力的提高 = uint16(0)
	E基础属性_受损的生命转为法力的减少 = uint16(0)
	E基础属性_按等级增加物理防御力   = uint16(0)
	E基础属性_按等级增加魔法防御力   = uint16(0)
	E基础属性_魔法使用减少       = uint16(0)
	E基础属性_增加攻击距离       = uint16(0)
	E基础属性_增加攻击范围       = uint16(0)
	E基础属性_减小冷却时间       = uint16(0)
)

type RunTime struct {
	tcp       net.Conn              //用户连接
	V角色       *S角色列表                //当前角色
	V物品列表     map[int32]*Struct物品列表 //角色物品列表 单一线程
	V位置登记     map[int32]*Struct位置登记 //用户记录的位置 单一线程
	V角色内容     *StructMap
	V技能列表     map[int32]*Struct技能列表 //实时同步
	V物理攻击属性   map[uint16]float32
	V物理魔法攻击属性 map[uint16]float32
	V魔法攻击属性   map[uint16]float32
	V新登录      bool        //在这个标志为真之前，将不会收到其它任何干扰信息
	txchan    chan []byte //发送的缓冲区
	在线检测      chan bool   //是否在线
	rxchan    chan *Buff  //接收的缓冲区
	exit      chan string //是否退出
	日期KEY     int
}

//这是地图中的一个内容，可以是角色，可以是怪物，也可以是一个物品
type StructMap struct {
	Id    int32
	V类别   E地图内容类别 //  0未知  1:怪物   2：NPC  3,物品  4:人物
	V空间   *S空间
	X     int32
	Y     int32
	Z     int32
	V方向   float32
	V内容   interface{} //怪物 人物 技能...
	V内容附加 interface{} //
	VChan chan *S交换数据

	V添加时间 time.Time
	检测时间  time.Time
	V基础属性 map[uint16]float32
	V额外属性 map[uint16]S额外属性

	V区域   *Struct区域定义    //在哪个小区域中
	V场景   map[int32]bool //地面物品，NPC ，怪物 全在这里边
	V攻击人  int32
	V等级   int
	V准备好  bool
	V是否死亡 bool //如果死亡，其它人就可以释放了
	V变身怪物 string

	V最后攻击者 *StructMap //如果死亡，最后一个攻击的人是谁
}
type E交换数据类型 = uint8

const (
	E交换数据类型_修改基础属性 = 1
	E交换数据类型_修改辅助技能 = 4
	E交换数据类型_攻击效果   = 6
	E交换数据类型_攻击     = 3
	E交换数据类型_增加经验   = 5
	E交换数据类型_透传数据   = 7
	E交换数据类型_查看摊位物品 = 8
	E交换数据类型_购买摊位物品 = 9
	E交换数据类型_查看摊位   = 10
	E交换数据类型_取消查看摊位 = 11
	E交换数据类型_有人路过   = 12
	E交换数据类型_怪物死亡   = 13
)

type S交换数据 struct {
	V类型  E交换数据类型
	V发送人 *StructMap
	V内容  interface{}
}

type S交换攻击效果 struct {
	V效果 uint8 //
	V掉血 float32
	V掉蓝 float32
}
type S交换角色通讯 struct {
	VCMD     byte
	VHeadlen int
	V信息      []byte
}
type S获取摊位物品列表 struct {
	V位置X uint8
	V位置Y uint8
	V物品  *Struct物品列表
	V单价  int64
}
type S查询摊位物品信息 struct {
	V位置X uint8
	V位置Y uint8
	V物品  chan *Struct物品列表
}
type S交换购买信息 struct {
	V位置X   uint8
	V位置Y   uint8
	V数量    uint16
	V购买金额  int64
	V购买的物品 chan *Struct物品列表
}

func (z *StructMap) F获取身边内容(内容ID int32) *StructMap {
	if _, ok := z.V场景[内容ID]; ok == false { //不在角色场景内
		return nil
	}
	return z.V空间.F获取内容(内容ID) //已经消失
}

//功能 检测并返回是否需要重新进行计算,能进来，说明一定相差一秒了
//返回
func (z *StructMap) F检测() (重新计算 bool, 改动参数 map[uint16]bool, 添加技能 []string, 删除技能 []string) {
	defer func() {
		z.检测时间 = time.Now()
	}()

	当前血量 := z.V基础属性[E基础属性_当前血量]
	if 当前血量 <= 0 { //如果没有血量了,就退出吧
		z.V基础属性[E基础属性_当前血量] = 0
		return
	}
	总血量 := z.V基础属性[E基础属性_总血量]
	改动参数 = make(map[uint16]bool)
	if 当前血量 != 总血量 {
		每秒加血 := z.V基础属性[E基础属性_生命恢复力]
		if 每秒加血 != 0 {
			当前血量 += 每秒加血
			if 当前血量 > 总血量 {
				当前血量 = 总血量
			}
			z.V基础属性[E基础属性_当前血量] = 当前血量
			改动参数[E基础属性_当前血量] = true
			重新计算 = true
		}
	}
	当前蓝量 := z.V基础属性[E基础属性_当前蓝量]
	总蓝量 := z.V基础属性[E基础属性_总蓝量]
	if 当前蓝量 != 总蓝量 {
		每秒加蓝 := z.V基础属性[E基础属性_魔法恢复力]
		if 每秒加蓝 != 0 {
			当前蓝量 += 每秒加蓝
			if 当前蓝量 > 总蓝量 {
				当前蓝量 = 总蓝量
			}
			z.V基础属性[E基础属性_当前蓝量] = 当前蓝量
			改动参数[E基础属性_当前蓝量] = true
			重新计算 = true
		}
	}
	//先检测有没有到的额外技能
	for k, v := range z.V额外属性 {
		if time.Now().Sub(v.V到期时间).Seconds() >= 0 {
			delete(z.V额外属性, k)
			删除技能 = append(删除技能, v.V技能英文名)
			for 修改ID, _ := range v.V添加内容 {
				改动参数[修改ID] = true
			}
			重新计算 = true
		}
	}
	return
}

//由于这是不能访问全局的
func (z *StructMap) F处理接收数据(交换数据 *S交换数据) (重新计算 bool, 改动参数 map[uint16]bool, 添加技能 []string, 删除技能 []string) {
	改动参数 = make(map[uint16]bool)
	switch 交换数据.V类型 {
	case E交换数据类型_修改基础属性: //修改基础属性 比如掉血, 掉蓝
		for 修改ID, 修改值 := range 交换数据.V内容.(map[uint16]float32) {
			原先, _ := z.V基础属性[修改ID]
			if (修改ID == E基础属性_当前血量) && (修改值 < 0) { // 这里是受损的生命转为法力的减少
				法力减少百分比 := z.V基础属性[E基础属性_受损的生命转为法力的减少]
				当前蓝量 := z.V基础属性[E基础属性_当前蓝量]
				if 法力减少百分比 > 0 {
					掉蓝 := 修改值 * 法力减少百分比 / 100
					if 当前蓝量 > 掉蓝 {
						当前蓝量 -= 掉蓝
						改动参数[E基础属性_当前蓝量] = true
						修改值 -= 掉蓝
					}
				}
				法力增加百分比 := z.V基础属性[E基础属性_受损的生命转为法力的提高]
				if 法力增加百分比 > 0 {
					加蓝 := 修改值 * 法力增加百分比 / 100
					当前蓝量 += 加蓝
					改动参数[E基础属性_当前蓝量] = true
					修改值 -= 加蓝
				}
			}
			if 修改值 != 0 {
				原先 += 修改值
				z.V基础属性[修改ID] = 原先
				改动参数[修改ID] = true
			}
			z.V攻击人 = 交换数据.V发送人.Id
		}
	case E交换数据类型_修改辅助技能: //修改额外属性
		额外 := 交换数据.V内容.(S额外属性)
		if v, ok := z.V额外属性[额外.V唯一ID]; ok == true {
			if v.V技能英文名 != 额外.V技能英文名 { //只有技能名称不一样的时候 才会删除
				删除技能 = append(删除技能, v.V技能英文名)
			}
		} else {
			添加技能 = append(添加技能, 额外.V技能英文名)
		}
		z.V额外属性[额外.V唯一ID] = 额外
		重新计算 = true
	case E交换数据类型_攻击: //被人攻击
		z.V最后攻击者 = 交换数据.V发送人
		A攻击属性 := 交换数据.V内容.(map[uint16]float32)
		B防御属性 := z.V基础属性
		var 物理防御力, 物理防御千分比, 物理最小攻击力, 物理最大攻击力, 物理暴击率 int32
		var 魔法防御力, 魔法防御千分比, 魔法最小攻击力, 魔法最大攻击力, 魔法暴击率 int32
		//对方的攻击能力
		物理最小攻击力 = int32(A攻击属性[E基础属性_最小物理攻击力])
		物理最大攻击力 = int32(A攻击属性[E基础属性_最大物理攻击力])
		物理暴击率 = int32(A攻击属性[E基础属性_必杀成功率])
		魔法最小攻击力 = int32(A攻击属性[E基础属性_最小魔法攻击力])
		魔法最大攻击力 = int32(A攻击属性[E基础属性_最大魔法攻击力])
		魔法暴击率 = int32(A攻击属性[E基础属性_魔法必杀成功率])
		//自己的防御属性
		物理防御力 = int32(B防御属性[E基础属性_物理防御力])
		物理防御千分比 = int32(B防御属性[E基础属性_物理防御千分比])
		魔法防御力 = int32(B防御属性[E基础属性_魔法防御力])
		魔法防御千分比 = int32(B防御属性[E基础属性_魔法防御千分比])

		物理差值 := 物理最大攻击力 - 物理最小攻击力
		if 物理差值 > 0 {
			物理差值 = int32(rand.Intn(int(物理差值)))
		} else {
			物理差值 = 0
		}
		魔法差值 := 魔法最大攻击力 - 魔法最小攻击力
		if 魔法差值 > 0 {
			魔法差值 = int32(rand.Intn(int(魔法差值)))
		} else {
			魔法差值 = 0
		}
		func() {
			var A物理掉血, A物理掉蓝 int32
			var A魔法掉血, A魔法掉蓝 int32
			var 效果 uint8
			defer func() {
				A掉血 := A物理掉血 + A魔法掉血
				A掉蓝 := A物理掉蓝 + A魔法掉蓝
				if A掉血 == 0 {
					A掉血 = 1
					效果 = 0
				}
				if 交换数据.V发送人.V类别 == E类别_人物 {
					F地图_发送信息(交换数据.V发送人, &S交换数据{
						V类型:  E交换数据类型_攻击效果,
						V发送人: z,
						V内容: S交换攻击效果{
							V效果: 效果,
							V掉血: float32(-A掉血),
							V掉蓝: 0,
						},
					})
				}
				if A掉血 != 0 {
					z.V基础属性[E基础属性_当前血量] -= float32(A掉血)
					改动参数[E基础属性_当前血量] = true
				}
				if A掉蓝 != 0 {
					z.V基础属性[E基础属性_当前蓝量] -= float32(A掉蓝)
					改动参数[E基础属性_当前蓝量] = true
				}
			}()
			A物理掉血 = int32(物理最小攻击力 + 物理差值)
			A物理掉血 = A物理掉血 * (int32(1000) - 物理防御千分比) / 1000
			A物理掉血 -= 物理防御力
			if A物理掉血 <= 0 {
				A物理掉血 = 0
			}
			闪躲率 := B防御属性[E基础属性_格挡成功率]
			if 闪躲率 > 0 {
				if rand.Intn(100) < int(闪躲率) { //被闪避了
					A物理掉血 = 0
				}
			}
			if 物理暴击率 != 0 {
				if rand.Intn(100) < int(物理暴击率) {
					A物理掉血 *= 2
					效果 = 3
				}
			}
			A魔法掉血 = int32(魔法最小攻击力 + 魔法差值)
			A魔法掉血 = A魔法掉血 * (int32(1000) - 魔法防御千分比) / 1000
			A魔法掉血 -= 魔法防御力
			if A魔法掉血 <= 0 {
				A魔法掉血 = 0
			}
			if 效果 == 3 { //如果物理效果已经暴击,则魔法同样暴击
				A魔法掉血 *= 2
			} else if 魔法暴击率 != 0 {
				if rand.Intn(100) < int(魔法暴击率) {
					A魔法掉血 *= 2
					效果 = 3
				}
			}
		}()
	default:
		panic(fmt.Sprintf("未知的交换数据类型:%d", 交换数据.V类型))
	}
	return
}
func F地图_发送信息(内容 *StructMap, 信息 *S交换数据) {
	go func(内容 *StructMap) {
		内容.VChan <- 信息
	}(内容)
}

const 方格大小 = 5000

var m_默认空间 *S空间

//返回一个临时的物品ID
func GetTempId() int32 {
	m_临时物品ID--
	return m_临时物品ID
}
func (rt *RunTime) 地图_显示内容(内容 *StructMap) {
	tx := CreateBuff(50)
	tx.AddInt32(内容.Id)
	tx.AddInt32(内容.X)
	tx.AddInt32(内容.Y)
	tx.AddFloat(内容.V方向)
	switch 内容.V类别 {
	case E类别_怪物: //怪物
		怪物 := 内容.V内容.(*Struct怪物列表)
		怪物信息, ok := m_怪物信息[怪物.V怪物中文名]
		if ok == false {
			panic("不可能")
		}
		tx.AddInt16(0x00B1)
		tx.AddByte(0x01)
		tx.AddStringAndLen(怪物信息.V英文名称)
		tx.AddStringAndLen(怪物信息.V中文名称)
		tx.AddInt32(怪物信息.V颜色) //01 02
		tx.AddFloat(怪物信息.V大小)
		tx.AddInt32(0) //随机值，可能是方向
		tx.AddInt32(int32(怪物信息.V等级))
		p := len(tx.Buff)
		tx.AddInt32(0x00)
		额外数量 := 0
		for _, v := range 内容.V额外属性 {
			tx.AddStringAndLen(v.V技能英文名)
			额外数量++
		}
		tx.Buff[p] = byte(额外数量)
		tx.AddInt32(0x01) // 一般是一，当前测试2
		tx.AddByte(0x00)
	case E类别_NPC: //NPC
		npc := 内容.V内容.(*SNpc列表)
		tx.AddInt16(0x00B3)
		tx.AddInt16(0x0001)
		tx.AddStringAndLen(npc.V英文名称) //添加样式长度
		tx.AddStringAndLen(npc.V中文名称) //添加名称长度
		tx.AddInt32(0x00)
		tx.AddStringAndLen("") //添加名称长度
		tx.AddFloat(npc.V大小)
		tx.AddStringAndLen(npc.V英文名称)
	case E类别_物品: //物品
		物品 := 内容.V内容.(*Struct物品列表)
		tx.AddInt16(0x00B4)
		rt.物品_简要信息(tx, 物品)
	case E类别_宠物:
		宠物 := 内容.V内容.(*S宠物列表)
		tx.AddInt16(0x00B1)
		tx.AddByte(0x01)
		tx.AddStringAndLen(宠物.V外观) //这里如果写NPC名字，怪物就不能被 鼠标点中
		tx.AddStringAndLen(宠物.V宠物名称)
		tx.AddInt32(01) //01 02  //
		tx.AddFloat(宠物.V大小)
		tx.AddInt32(0) //随机值，可能是方向
		tx.AddInt32(宠物.V等级)
		p := len(tx.Buff)
		tx.AddInt32(0x00)
		额外数量 := 0
		for _, v := range 内容.V额外属性 {
			tx.AddStringAndLen(v.V技能英文名)
			额外数量++
		}
		tx.Buff[p] = byte(额外数量)
		tx.AddInt32(0x01) // 一般是一，当前测试2
		tx.AddByte(0x00)
	case E类别_人物: //人物
		if rt.V角色内容.Id == 内容.Id { //自己，不用处理
			return
		}
		人物 := 内容.V内容.(*S角色列表)
		tx.AddInt16(0x00B0)
		tx.AddByte(0x01)

		tx.AddStringAndLen(人物.V名字)
		tx.AddStringAndLen(人物.V种族)
		tx.AddBool(人物.V性别)
		tx.AddStringAndLen(人物.V职业)
		tx.AddByte(0x02)
		tx.AddInt32(0x00)

		tx.AddStringAndLen("")
		tx.AddStringAndLen("")
		tx.AddStringAndLen("")
		trt, ok := m_RunTime.Load(人物.V名字)
		if ok == true {
			trt0 := trt.(*RunTime)

			tx.AddStringAndLen(人物.V所属地)
			tx.AddStringAndLen(trt0.角色_获取行会名称())
			tx.AddStringAndLen("")
			tx.AddStringAndLen(人物.V发型发色)
			tx.AddStringAndLen("")
			tx.AddStringAndLen("HunterSuit")
			tx.AddInt32(0x00) //06 //ff
			tx.AddByte(0x01)
			tx.AddByte(0x00) //01
			tx.AddByte(0x01)
			tx.AddByte(0x00)
			if 人物.V状态 == E角色状态_摆摊 {
				tx.AddByte(0x01)
				tx.AddStringAndLen(人物.V状态内容.(string))
			} else {
				tx.AddByte(0x00)
				tx.AddStringAndLen("")
			}

			tx.AddByte(0x01)
			tx.AddInt32(-5469)

			for wz := uint8(0); wz < 32; wz++ {
				v, ok := 人物.V佩戴物品[wz]
				if ok == false {
					tx.AddByte(0)
					continue
				}
				wp, ok := trt0.V物品列表[v]
				if ok == false || wp == nil {
					tx.AddByte(0)
					continue
				}
				tx.AddByte(1)
				rt.物品_简要信息(tx, wp)
			}
			p := len(tx.Buff)
			tx.AddInt32(0x00)
			额外数量 := 0
			for _, v := range 内容.V额外属性 {
				tx.AddStringAndLen(v.V技能英文名)
				额外数量++
			}
			tx.Buff[p] = byte(额外数量)
			tx.AddInt32(0x00)
			tx.AddInt32(0x01)
			if len(内容.V变身怪物) == 0 {
				tx.AddInt16(0)
				tx.AddStringAndLen("")
				tx.AddStringAndLen("")
			} else {
				tx.AddInt16(1)
				变身 := m_怪物信息[内容.V变身怪物]
				tx.AddStringAndLen(变身.V英文名称)
				tx.AddStringAndLen(变身.V英文名称)
			}
			tx.AddBytes(String2Byte("4E 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"))
		}
	case E类别_技能: //技能
		技能信息 := 内容.V内容.(*Struct技能信息)
		tx.AddInt16(0x00B6)
		tx.AddInt32(技能信息.V未知1)
		tx.AddStringAndLen(技能信息.V技能英文名)
		tx.AddInt32(技能信息.V未知2)

	default:
		panic("未知场景内容")
	}
	rt.AddHeadSend10(0x11, [][]byte{tx.Buff[:16], tx.Buff[16:]})
}

//功能 删除一个物品
func (rt *RunTime) 地图_消失内容(id int32) {
	tx := CreateBuff(4)
	tx.AddInt32(id)
	rt.AddHeadSend10(0x10, [][]byte{tx.Buff})
}
